package editor.code;

import static editor.Configuration.INDENT;
import static editor.Configuration.UNINDENT;
import event.TextReplacementEvent;
import event.UndoSet;
import event.Undoable;
import model.caret.TextCaret;
import model.code.Function;
import model.code.line.MultiLineList;
import observer.ObservedBoolean;
import observer.ObservedInt;

import javax.swing.event.AncestorEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;

/**
 * All text or code editing panels extend from this class.
 *
 * @author Neil Dickson
 */
public abstract class CodePanel extends LineField {
	public static Stroke			SIDE_LINE_STROKE 		= new BasicStroke(1,BasicStroke.CAP_BUTT,BasicStroke.JOIN_BEVEL,0,new float[]{1,2},0);
	public static Color				SIDE_LINE_COLOUR 		= Color.LIGHT_GRAY;
	public static Color				ERROR_COLOUR	 		= Color.RED;
	public static Font				ERROR_FONT		 		= BOLD_FONT;

	private static int				LEFT_MARGIN				= 16;

	protected	Function			function;

	protected CodePanel(Function function) {
		super(function.getCode(),function.getFile().getCaretRef(),false);
		this.function = function;
		insets.left = LEFT_MARGIN;
		((MultiLineList)lines).addObserver(this);
	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		((Graphics2D)g).setStroke(SIDE_LINE_STROKE);
		g.setColor(SIDE_LINE_COLOUR);
		g.drawLine(insets.left-1,0,insets.left-1,getHeight());
	}

	public Undoable keyPressedCommon(KeyEvent e, ObservedInt undoCombiningRef, ObservedBoolean killAutocompleteRef) {
		Undoable event = null;
		if (caretRef.isTextCaret(lines)) {
			TextCaret caret = (TextCaret)caretRef.get();
			// Unindent Line
			if (UNINDENT.isTriggered(e)) {
				int firstLine = caret.getFirstLine();
				int lastLine = caret.getLastLine();
				int firstLineCol = caret.getFirstLineCol();
				int lastLineCol = caret.getLastLineCol();

				ArrayList<TextReplacementEvent> events = new ArrayList<TextReplacementEvent>(lastLine-firstLine+1);
				// Remove initial tabs
				for (int lineNum=firstLine;lineNum<=lastLine;++lineNum) {
					if (lines.get(lineNum).toString(0,1).charAt(0)=='\t') {
						events.add(lines.replace(lineNum,0,lineNum,1,""));
					}
				}

				if (events.size()==1) {
					event = events.get(0);
				}
				else {
					event = new UndoSet(events.toArray(new Undoable[events.size()]),"Unindent in "+function.getName());
				}

				firstLineCol = Math.max(0,firstLineCol-1);
				lastLineCol = Math.max(0,lastLineCol-1);
				if (firstLine==caret.getHeadLine()) {
					caret.set(firstLine,firstLineCol,lastLine,lastLineCol,LineRenderer.getX(lines.get(firstLine),firstLineCol));
				}
				else {
					caret.set(lastLine,lastLineCol,firstLine,firstLineCol,LineRenderer.getX(lines.get(lastLine),lastLineCol));
				}
			}
			// Indent Line
			else if (INDENT.isTriggered(e) && caret.getHeadLine()!=caret.getTailLine()) {
				int firstLine = caret.getFirstLine();
				int lastLine = caret.getLastLine();
				int firstLineCol = caret.getFirstLineCol();
				int lastLineCol = caret.getLastLineCol();

				ArrayList<TextReplacementEvent> events = new ArrayList<TextReplacementEvent>(lastLine-firstLine+1);
				// Add initial tabs
				for (int lineNum=firstLine;lineNum<=lastLine;++lineNum) {
					events.add(lines.insert(lineNum,0,"\t"));
				}

				if (events.size()==1) {
					event = events.get(0);
				}
				else {
					event = new UndoSet(events.toArray(new Undoable[events.size()]),"Indent in "+function.getName());
				}

				++firstLineCol;
				++lastLineCol;
				if (firstLine==caret.getHeadLine()) {
					caret.set(firstLine,firstLineCol,lastLine,lastLineCol,LineRenderer.getX(lines.get(firstLine),firstLineCol));
				}
				else {
					caret.set(lastLine,lastLineCol,firstLine,firstLineCol,LineRenderer.getX(lines.get(lastLine),lastLineCol));
				}
			}
			else {
				event = super.keyPressedCommon(e,undoCombiningRef,killAutocompleteRef);
			}
		}
		return event;
	}

	public void ancestorAdded(AncestorEvent event) {
		super.ancestorAdded(event);
		((MultiLineList)lines).addObserver(this);
	}

	public void ancestorRemoved(AncestorEvent event) {
		super.ancestorRemoved(event);
		((MultiLineList)lines).removeObserver(this);
	}
}
